CDK TypeScriptを用いてAWS AppSync試してみた

CDK TypeScriptを用いてAWS AppSync試してみた

Clock Icon2024.09.08

リテールアプリ共創部のるおんです。先日、AppSyncとGraphQLを用いたアプリケーションの開発に携わる機会がありました。その際、初めてAppSyncを使用したので入門してみました。今回は、その際に実装したAppSyncとDynamoDBを組み合わせた簡単なAPIの実装方法を共有したいと思います。

AppSyncとは

AWS AppSyncは、AWSが提供するマネージドGraphQLサービスです。リアルタイムデータ同期、オフラインプログラミング機能を備え、大規模なアプリケーションの開発を容易にします。また、DynamoDB、Aurora、OpenSearchなど、様々なAWSサービスと簡単に統合できるのが特徴です。

全体像

今回は、AWS CDKを用いてAppSyncとDynamoDBを連携させたシンプルなGraphQL APIを構築します。使用した言語はTypeScriptです。
また、基本的なGraphQLクエリの構築に慣れていることを前提としています。

今回の構成では、AppSyncがGraphQL APIのエンドポイントとして機能し、DynamoDBがデータストアとして使用されます。
DynamoDBテーブルでは、「title(タイトル)」と「author(著者)」の情報を保存するシンプルな構造のテーブルを作成します。

スクリーンショット 2024-09-08 2.25.58

CDKでなく、マネジメントコンソールから実装する場合、以下の記事が参考になりました。
https://dev.classmethod.jp/articles/touching-aws-appsync-just-a-little/

CDKで実装してみる

それでは、この構成をCDKで実装していきましょう。TypeScriptが使用可能で、AWS CDKを用いて手元のマシンからデプロイができる環境が整っていることを前提に進めていきます。

1. セットアップ

まず、プロジェクトを作成してCDKを開始します。

bash
mkdir appsync-demo
cd appsync-demo
cdk init app --language typescript

2. スタックの実装

早速ですが以下が完全なスタックコードです。

lib/appsync-demo-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

export class AppsyncDemoStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    // AppSync API の作成
    const api = new appsync.GraphqlApi(this, 'Api', {
      name: 'demo-api',
      definition: appsync.Definition.fromFile('schema.graphql'),
    });

    // DynamoDB テーブルの作成
    const table = new dynamodb.Table(this, 'BooksTable', {
      tableName: 'books',
      partitionKey: { name: 'title', type: dynamodb.AttributeType.STRING },
    });

    // DynamoDB をデータソースとして追加
    const dataSource = api.addDynamoDbDataSource('ItemsDataSource', table);

    // リゾルバーの追加
    // getBooks リゾルバー
    dataSource.createResolver("getBooksResolver", {
      typeName: 'Query',
      fieldName: 'getBooks',
      requestMappingTemplate: appsync.MappingTemplate.dynamoDbScanTable(),
      responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultList(),
    });

    // createBook リゾルバー
    dataSource.createResolver("createBookResolver", {
      typeName: 'Mutation',
      fieldName: 'createBook',
      requestMappingTemplate: appsync.MappingTemplate.dynamoDbPutItem(
        appsync.PrimaryKey.partition('title').is('input.title'),
        appsync.Values.projecting('input')
      ),
      responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultItem(),
    });
  }
}

'demo-api'という名前のAppSync APIを作成し、スキーマはschema.graphqlファイルから読み込むようにしています。
また、booksという名前のDynamoDBテーブルを作成し、パーティションキーとして文字列型のtitleを指定しています。
作成したDynamoDBテーブルをAppSync APIのデータソースとして追加し、getBooksクエリととcreateBookミューテーションに対応するリゾルバーを作成しています。
このコードにより、基本的なCRUD操作(Create, Read)が可能なGraphQL APIが構築されます。ただし、更新(Update)と削除(Delete)の操作は含まれていないため、必要に応じて追加することができます。

3. GraphQLスキーマの定義

schema.graphqlファイルを作成し、以下のスキーマを定義します。

schema.graphql
type Book {
  title: String!
  author: String!
}

input CreateBookInput {
  title: String!
  author: String!
}

type Query {
	getBooks: [Book]
}

type Mutation {
  createBook(input: CreateBookInput!): Book
}

このGraphQLスキーマは以下の要素で構成されています:

  • Book 型:
    title と author の2つのフィールドを持つ本の基本構造を定義します。
    両フィールドとも String! 型で、非nullであることを示しています。
  • reateBookInput 入力型:
    新しい本を作成する際に使用する入力データの構造を定義します。
    Book 型と同じフィールドを持ちますが、入力データとして使用されます。
  • Query 型:
    getBooks クエリを定義し、これは Book 型の配列を返します。
    このクエリにより、すべての本のリストを取得できます。
  • Mutation 型:
    createBook ミューテーションを定義します。
    CreateBookInput! 型の input 引数を受け取り、新しく作成された Book を返します。

このスキーマにより、クライアントは本のリストを取得したり(getBooksクエリ)、新しい本を追加したり(createBookミューテーション)することができます。スキーマはAPIの契約として機能し、クライアントとサーバー間でデータ構造と操作を明確に定義します。

動作確認

デプロイ

以下のコマンドでスタックをデプロイします。

bash
cdk deploy

デプロイが完了すると、AppSyncのAPIが作成されていると思います。また、DynamoDBにbookテーブルが作成されており、ここにはまだ何も項目がないことを確認してください。

マネジメントコンソールからの実行

AWS Management ConsoleにログインしてAppSyncのダッシュボードに移動します。
作成したAPIを選択し、「クエリ」タブを開きます。
このタブを開くと、以下の画像のようにGraphQL操作を直接テストできるインターフェースが提供されます。
スクリーンショット 2024-09-08 1.41.08

まだ、何もクエリとクエリ変数が設定されていないと思うので、以下のコードをそれぞれ記述してみます。
クエリ:

query getBooks {
  getBooks {
    title
    author
  }
}

mutation createBook($createbookinput: CreateBookInput!) {
  createBook(input: $createbookinput) {
    title
    author
  }
}

クエリ変数:

{
  "createbookinput": {
    "title": "AppSync入門!",
    "author": "るおん"
  }
}

設定ができたら、実行してみます。

「実行する」ボタンを押して、createBookを選択します。すると、クエリ変数として渡した値でDynamoDBテーブル内に項目が作成されます。
以下の赤線内ように実行結果のレスポンスが返却されています。そしてDynamoDBを確認すると、実際に項目が追加されていることがわかります。
スクリーンショット 2024-09-08 1.48.31
スクリーンショット 2024-09-08 1.50.47

次に、「実行する」ボタンからgetBooksを選択してみます。すると、DynamoDB内のbooksテーブル内の項目が返却されて先ほど作成した項目が返却されることが確認できると思います。
スクリーンショット 2024-09-08 1.53.55

無事APIのクエリが実行できることが確認できました!

Node.jsからの実行

次は、Node.jsからAxiosを用いてAPIを実行してみます。API URLとAPIキーを自身のものに置き換えてください。

index.js
const axios = require("axios");

axios.post("https://xxxxxxxxxxxxx.appsync-api.ap-northeast-1.amazonaws.com/graphql", {
  query: /* GraphQL */`
    query getBooks {
      getBooks {
        title
        author
      }
    }`
}, {
  headers: {
    'x-api-key': "xxx-xxxxxxxxxxxxxxxxxx",
  },
}).then((response) => {
  console.log(JSON.stringify(response.data));
}).catch((error) => {
  console.error(error);
})

このスクリプトを実行すると、マネジメントコンソールでテストしたように、booksテーブル内の項目が返却されます。

bash
node index.js
# 出力:{"data":{"getBooks":[{"title":"AppSync入門!","author":"るおん"}]}}

AppSyncを用いてGraphQL APIを無事実行できることが確認できました!

おわりに

今回はCDK TypeScriptを使ってAppSyncとDynamoDBを組み合わせたシンプルなGraphQL APIを構築しました。マネジメントコンソールとNode.jsクライアントの両方から動作確認を行い、AppSyncの基本的な使い方を説明しました。
今後は、より複雑なスキーマやリゾルバー、認証機能の追加など、AppSyncの高度な機能にも挑戦してみたいと思います。

参考になれば幸いです。

参考

AWS AppSync
AWS CDK - AppSync Module
GraphQL

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.